from PyQt5 import QtBluetooth, QtCore
from PyQt5.QtBluetooth import QBluetoothUuid, QLowEnergyCharacteristic


class myBLE(QtCore.QObject):
    DiscoverySignal = QtCore.pyqtSignal(QtBluetooth.QBluetoothDeviceInfo)
    ServiceFoundSignal = QtCore.pyqtSignal(QBluetoothUuid)
    CharacteristicSignal = QtCore.pyqtSignal(QLowEnergyCharacteristic)
    ReadSignal = QtCore.pyqtSignal(QtCore.QByteArray)
    TestSignal = QtCore.pyqtSignal(str)

    def __init__(self):
        super(myBLE, self).__init__()
        self.discovery_agent: QtBluetooth.QBluetoothDeviceDiscoveryAgent = None
        self.service: QtBluetooth.QLowEnergyService = None
        self.low_energy_controller: QtBluetooth.QLowEnergyController = None

    def start_scan(self):
        try:
            self.discovery_agent = QtBluetooth.QBluetoothDeviceDiscoveryAgent()
            self.discovery_agent.setLowEnergyDiscoveryTimeout(5000)
            self.discovery_agent.deviceDiscovered.connect(self.device_discovered)
            self.discovery_agent.finished.connect(self.discovery_finished)

            self.discovery_agent.start()
            if self.discovery_agent.isActive():
                self.TestSignal.emit("scanning")
        except Exception as e:
            return str(e)
        else:
            return None

    def device_discovered(self, info: QtBluetooth.QBluetoothDeviceInfo):
        # 我们只关心BLE，只对搜索到的BLE设备做处理
        if info.coreConfigurations() & QtBluetooth.QBluetoothDeviceInfo.CoreConfiguration.LowEnergyCoreConfiguration:
            self.TestSignal.emit("find:" + info.name())
            self.DiscoverySignal.emit(info)

    def discovery_finished(self):
        self.TestSignal.emit("scan finished")

    def connect_device(self, dev: QtBluetooth.QBluetoothDeviceInfo):
        if self.low_energy_controller is not None:
            if self.low_energy_controller.state() == QtBluetooth.QLowEnergyController.ControllerState:
                self.low_energy_controller.disconnectFromDevice()

        del self.low_energy_controller
        self.low_energy_controller = QtBluetooth.QLowEnergyController.createCentral(dev)
        self.low_energy_controller.connected.connect(self.controller_connected)
        self.low_energy_controller.disconnected.connect(self.controller_disconnected)
        self.low_energy_controller.serviceDiscovered.connect(self.service_discovered)
        self.low_energy_controller.discoveryFinished.connect(self.service_discover_finished)
        self.low_energy_controller.error.connect(self.controller_error)
        self.low_energy_controller.connectToDevice()

    def controller_connected(self):
        self.TestSignal.emit("device connected success， start discovery services...")
        self.low_energy_controller.discoverServices()

    def controller_disconnected(self):
        self.TestSignal.emit("device disconnected!")

    def service_discovered(self, uuid: QtBluetooth.QBluetoothUuid):
        self.TestSignal.emit("discovered one service:" + uuid.toString())
        self.ServiceFoundSignal.emit(uuid)

    def service_discover_finished(self):
        self.TestSignal.emit("service discovery finished")

    def controller_error(self, err_msg):
        self.TestSignal.emit("Error:" + str(err_msg))

    # 连接服务
    def connect_service(self, uuid: QtBluetooth.QBluetoothUuid):
        del self.service
        self.service = self.low_energy_controller.createServiceObject(uuid)

        if self.service:
            if self.service.state() == QtBluetooth.QLowEnergyService.ServiceState.DiscoveryRequired:
                self.service.stateChanged.connect(self.state_changed)
                self.service.characteristicChanged.connect(self.characteristic_changed)
                self.service.characteristicRead.connect(self.characteristic_read)
                self.service.characteristicWritten.connect(self.characteristic_written)
                self.service.descriptorWritten.connect(self.descriptor_written)
                self.service.descriptorRead.connect(self.descriptor_read)

                self.service.discoverDetails()
            elif self.service.state() == QtBluetooth.QLowEnergyService.ServiceState.ServiceDiscovered:
                self.state_changed(self.service.state())
        else:
            self.TestSignal.emit("ERR: Cannot open service")

    def state_changed(self, state: QtBluetooth.QLowEnergyService.ServiceState):
        if state == QtBluetooth.QLowEnergyService.ServiceState.DiscoveringServices:
            self.TestSignal.emit("state changed to: DiscoveringServices")
        elif state == QtBluetooth.QLowEnergyService.ServiceState.ServiceDiscovered:
            self.TestSignal.emit("state changed to: ServiceDiscovered")
            characteristics = self.service.characteristics()
            for ch in characteristics:
                self.CharacteristicSignal.emit(ch)

    def characteristic_changed(self, ch: QtBluetooth.QLowEnergyCharacteristic, value: QtCore.QByteArray):
        self.ReadSignal.emit(value)

    def characteristic_read(self, ch: QtBluetooth.QLowEnergyCharacteristic, value: QtCore.QByteArray):
        self.ReadSignal.emit(value)

    def characteristic_written(self, ch: QtBluetooth.QLowEnergyCharacteristic, value: QtCore.QByteArray):
        self.TestSignal.emit("BLE设备-" + ch.uuid().toString() + "\t 特性写入值：" + str(value.toHex()))

    def descriptor_written(self, descriptor: QtBluetooth.QLowEnergyDescriptor, value: QtCore.QByteArray):
        self.TestSignal.emit(descriptor.name() + " - " + descriptor.uuid().toString() + "\t 描述符写入值：" + str(value.toHex()))

    def descriptor_read(self, descriptor: QtBluetooth.QLowEnergyDescriptor, value: QtCore.QByteArray):
        self.TestSignal.emit(descriptor.name() + " - " + descriptor.uuid().toString() + "\t 描述符读取值：" + str(value.toHex()))

    def read_characteristic(self, characteristic: QtBluetooth.QLowEnergyCharacteristic):
        try:
            if characteristic.isValid():
                print(str(characteristic.properties()))
                if characteristic.properties() & QtBluetooth.QLowEnergyCharacteristic.PropertyType.Read:
                    self.TestSignal.emit("read:")
                    self.service.readCharacteristic(characteristic)

                if characteristic.properties() & QtBluetooth.QLowEnergyCharacteristic.PropertyType.Notify:
                    self.TestSignal.emit("Notify")
                    descriptor = characteristic.descriptors()
                    for d in descriptor:
                        self.service.writeDescriptor(d, QtCore.QByteArray.fromHex(str("0100").encode()))
        except Exception as e:
            self.TestSignal.emit("Error:" + str(e))

    def write_characteristic(self, characteristic: QtBluetooth.QLowEnergyCharacteristic, data: str):
        try:
            if characteristic.isValid():
                if characteristic.properties() == QLowEnergyCharacteristic.PropertyType.Write \
                        or characteristic.properties() == QLowEnergyCharacteristic.PropertyType.WriteNoResponse:
                    self.TestSignal.emit("write:")
                    self.service.writeCharacteristic(characteristic, QtCore.QByteArray(data))

        except Exception as e:
            self.TestSignal.emit("Error:" + str(e))
